#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"
#include <getopt.h>

#ifndef RAND_NUM
#define RAND_NUM 100
#endif

#ifndef SUPP
#define SUPP 10
#endif

int arrRemove(int *arr, int *len, int pos)
{
	int i;

	for (i = pos; i < *len - 1; i++) {
		arr[i] = arr[i + 1];
	}

	*len = *len - 1;

	return 0;
}

/*
 * Logs current game state to file.
 */
int outputState(struct gameState *state, FILE *fp)
{
	int i, j;

	fprintf(fp, "****GAME STATE****\n");
	fprintf(fp, "Number of Players: %d\n", state->numPlayers);
	fprintf(fp, "Supply Count:\n");
	
	for(i = 0; i < treasure_map + 1; i++) {
		fprintf(fp, "	Card: %d Amount: %d\n", i, state->supplyCount[i]);
	}
	
	fprintf(fp, "Embargo Tokens\n");

	for(i = 0; i < treasure_map + 1; i++) {
		fprintf(fp, "	Card: %d Tokens: %d\n", i, state->embargoTokens[i]);
	}

	fprintf(fp, "Outpost Played Status: %d\n", state->outpostPlayed);
	fprintf(fp, "Outpost Turn: %d\n", state->outpostTurn);
	fprintf(fp, "Phase: %d\n", state->phase);
	fprintf(fp, "Actions: %d\n", state->numActions);
	fprintf(fp, "Coins: %d\n", state->coins);
	fprintf(fp, "Buys: %d\n", state->numBuys);
	fprintf(fp, "HANDS\n");

	for(i = 0; i < state->numPlayers; i++) {

		fprintf(fp, "	Player %d Hand Count: %d\n    Hand: ", i, state->handCount[i]);
		
		for(j = 0; j < state->handCount[i]; j++) {
			fprintf(fp, "%d, ", state->hand[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "DECKS\n");
	for(i = 0; i < state->numPlayers; i++) {
		fprintf(fp, "	Player %d Deck Count: %d\n    Deck: ", i, state->deckCount[i]);
		
		for(j = 0; j < state->deckCount[i]; j++) {
			fprintf(fp, "%d, ", state->deck[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "DISCARDS\n");
	for(i = 0; i < state->numPlayers; i++) {
		fprintf(fp, "	Player %d Discard Count: %d\n    Discard: ", i, state->discardCount[i]);
		
		for(j = 0; j < state->discardCount[i]; j++) {
			fprintf(fp, "%d, ", state->discard[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "Played Count: %d\n    Played Cards: ", state->playedCardCount);
	for(i = 0; i < state->playedCardCount; i++) {
		fprintf(fp, "%d, ", state->playedCards[i]);
	}
	fprintf(fp, "\n");

	return 0;
}


/*
 * Generates a random number of players, seed, and kingdom cards.
 */
int randomizeInitGame(int *players, int kingdomCards[SUPP])
{
	int i, *arr, len, selectPos;

	// Generate random number of players
	*players = floor(Random() * (MAX_PLAYERS - 1)) + 2;

	// Put all types of cards into array for selection
	len = treasure_map - adventurer + 1;
	arr = (int *) malloc(sizeof(int) * len);

	for (i = 0; i < len; i++) {
		arr[i] = adventurer + i;
	}

	// Select 10 of those cards for the kingdom cards
	for (i = 0; i < SUPP; i++) {
		selectPos = floor(Random() * len);
		kingdomCards[i] = arr[selectPos];
		arrRemove(arr, &len, selectPos);
	}

	free(arr);
	arr = NULL;

	return 0;
}

/*
 * Choose valid coin, vp, or action in supply
 */
int playFeast(struct gameState *state, int kingdomCards[SUPP], int *choice1)
{
	int validChoice = 0;
	int selectCardType;

	// 33% kingdom card, 16% silver, 17% copper, 16% duchy, 17% estate
	while (!validChoice) {
		selectCardType = floor(Random() * 100);

		if (selectCardType > 66) {
			*choice1 = kingdomCards[(int) floor(Random() * SUPP)];
		} else if (selectCardType > 50) {
			*choice1 = silver;
		} else if (selectCardType > 33) {
			*choice1 = copper;
		} else if (selectCardType > 17) {
			*choice1 = duchy;
		} else {
			*choice1 = estate;
		}

		// choice selected must cost less than or equal to 5
		// and also make sure supply count is greater than 0
		if (getCost(*choice1) <= 5 && state->supplyCount[*choice1] > 0) {
			validChoice = 1;
		}
	}

	return 0;
}

int playMine(struct gameState *state, int *choice1, int *choice2)
{
	int i;

	if (state->handCount[state->whoseTurn] > 1) {
		// Make choice a copper card
		if (state->supplyCount[silver] > 0) {
			for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
				if (state->hand[state->whoseTurn][i] == copper) {
					*choice1 = i;
					*choice2 = silver;
					return 0;
				}
			}
		}

		// Make choice a silver card
		if (state->supplyCount[gold] > 0) {
			for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
				if (state->hand[state->whoseTurn][i] >= silver) {
					*choice1 = i;
					return 0;
				}
			}
		}
	}

	return -1;
}

int playRemodel(struct gameState *state, int kingdomCards[SUPP], int handPos, int *choice1, int *choice2)
{
	int cardPos, cost, selectCard, attempt = 0, validChoice = 0;

	if (state->handCount[state->whoseTurn] > 1) {
		while (!validChoice) {
			cardPos = floor(Random() * state->handCount[state->whoseTurn]);

			if (cardPos != handPos) {
				*choice1 = cardPos;
				validChoice = 1;
			}
		}
		
		while (1) {
			cost = getCost(state->hand[state->whoseTurn][*choice1] + 2);

			if (cost >= 8 && state->supplyCount[province] > 0) {
				*choice2 = province;
				break;
			} else if (cost >= 6 && state->supplyCount[gold] > 0) {
				*choice2 = gold;
				break;
			} else if (cost <= 3 && state->supplyCount[estate] > 0) {
				*choice2 = estate;
				break;
			} else if (cost <= 3 && state->supplyCount[copper] > 0) {
				*choice2 = copper;
				break;
			} else {
				selectCard = kingdomCards[(int) floor(Random() * SUPP)];

				if (getCost(selectCard) <= cost && state->supplyCount[selectCard]) {
					*choice2 = selectCard; 
					break;
				} else {
					attempt++;
				}

				if (attempt >= 10) {
					return -1;
				}
			}
		}
		return 0;
	}
	return -1;
}

int playBaron(struct gameState *state, int *choice1)
{
	int i;

	for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
		if (state->hand[state->whoseTurn][i] == estate) {
			*choice1 = 1;
			return 0;
		}
	}
	
	*choice1 = 0;
	return 0;
}

int playSteward(struct gameState *state, int handPos, int *choice1, int *choice2, int *choice3)
{
	int choice;

	//0, 1, or 2
	choice = floor(Random() * 3);

	switch (choice) {

	case 0:
	case 1:
		break;
	case 2:
		if (state->handCount[state->whoseTurn] >= 3) {
			while (1) {
				*choice3 = floor(Random() * state->handCount[state->whoseTurn]);
				if (*choice3 != handPos) {
					break;
				}
			}
		} else {
			*choice3 = -1;
		}

		if (state->handCount[state->whoseTurn] >= 2) {
			while (1) {
				*choice2 = floor(Random() * state->handCount[state->whoseTurn]);
				if (*choice2 != handPos && *choice2 != *choice3) {
					break;
				}
			}
		} else {
			*choice2 = -1;
		}

		break;
	default:
		return -1;

	}

	return 0;
}

int playAmbassador(struct gameState *state, int handPos, int *choice1, int *choice2)
{
	int i, cardCount = 0;

	// Choice1 = card in hand to be tossed
	// Choice2 = how may to toss

	if (state->handCount[state->whoseTurn] < 2) {
		return -1;
	}
	
	while (1) {
		*choice1 = floor(Random() * state->handCount[state->whoseTurn]);
		if (*choice1 != handPos) {
			break;
		}
	}

	for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
		if (state->hand[state->whoseTurn][i] == *choice1) {
			cardCount++;
		}
	}

	if (cardCount >= 2) {
		*choice2 = floor(Random() * 3);
	} else {
		*choice2 = floor(Random() * 2);
	}

	return 0;
}

int playEmbargo(struct gameState *state, int kingdomCards[SUPP], int *choice1)
{
	int selectCardType;

	// 33% kingdom card, 16% silver, 17% copper, 16% duchy, 17% estate
	selectCardType = floor(Random() * 100);

	if (selectCardType > 66) {
		*choice1 = kingdomCards[(int) floor(Random() * SUPP)];
	} else if (selectCardType > 50) {
		*choice1 = silver;
	} else if (selectCardType > 33) {
		*choice1 = copper;
	} else if (selectCardType > 17) {
		*choice1 = duchy;
	} else {
		*choice1 = estate;
	}

	return 0;
}

int playSalvager(struct gameState *state, int handPos, int *choice1)
{
	int cardSelect;

	if (state->handCount[state->whoseTurn] <= 1) {
		return -1;
	}

	while (1) {
		cardSelect = floor(Random() * state->handCount[state->whoseTurn]);
		if (cardSelect != handPos) {
			break;
		}
	}

	return 0;
}

int doAction(struct gameState *state, int kingdomCards[SUPP], FILE *f) {
	int i, actions, result, willAct;
	int selectCard, selectCardPos; 
	int *playableCards, playableCardCount = 0;
	int handPos, choice1, choice2, choice3;

	playableCards = (int *) malloc(sizeof(int) * 10);
	actions = state->numActions;

	// Count number of playable cards in hand & save it in array
	for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
		if (state->hand[state->whoseTurn][i] > gold && state->hand[state->whoseTurn][i] != gardens) {
			playableCards[playableCardCount] = state->hand[state->whoseTurn][i];
			playableCardCount++;
		}
	}

	// Set chance to act
	willAct = floor(Random() * 100);

	while (actions > 0 && willAct >= 10 && playableCardCount > 0) {
		result = 0;
		choice1 = -1;
		choice2 = -1;
		choice3 = -1;

		// Select playable card to play
		selectCardPos = floor(Random() * playableCardCount);
		selectCard = playableCards[selectCardPos];

		// Ignore cards set to unplayable
		while (selectCard == -1) {
			selectCardPos = floor(Random() * playableCardCount);
			selectCard = playableCards[selectCardPos];
		}


		// Find selected card to play
		for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
			if (state->hand[state->whoseTurn][i] == selectCard) {
				handPos = i;
			}
		}

		switch (selectCard) {

		case feast:
			result = playFeast(state, kingdomCards, &choice1);
			break;
		
		case mine:
			result = playMine(state, &choice1, &choice2);
			break;

		case remodel:
			result = playRemodel(state, kingdomCards, handPos, &choice1, &choice2);
			break;
		
		case baron:
			result = playBaron(state, &choice1);
			break;

		case steward:
			result = playSteward(state, handPos, &choice1, &choice2, &choice3);
			break;
		
		case ambassador:
			result = playAmbassador(state, handPos, &choice1, &choice2);
			break;

		case embargo:
			result = playEmbargo(state, kingdomCards, &choice1);
			break;

		case salvager:
			result = playSalvager(state, handPos, &choice1);
			break;

		default:
			break;
		}

		if (result == 0) {
			fprintf(f, "Playing Card #%d\n", selectCard);
			result = playCard(handPos, choice1, choice2, choice3, state);
			actions = state->numActions;
		}

		//Set card to unplayable
		arrRemove(playableCards, &playableCardCount, selectCardPos);

		willAct = floor(Random() * (100 + 1));
	}

	// Free malloc space
	free(playableCards);
	playableCards = NULL;

	return 0;
}

/*
 * Buy cards for player
 */
int doBuy(struct gameState *state, int kingdomCards[SUPP], FILE *f)
{
	int coins, buys, choice, willBuy, attempts;

	// Get turn status
	coins = state->coins;
	buys = state->numBuys;

	willBuy = floor(Random() * 100);

	while (buys > 0 && willBuy >= 10) {

		if (coins >= 8 && state->supplyCount[province] > 0) {
			choice = province;
		} else if (coins >= 6 && state->supplyCount[gold] > 0) {
			choice = gold;
		} else if (coins <= 3 && state->supplyCount[estate] > 0) {
			choice = estate;
		} else if (coins <= 3 && state->supplyCount[copper] > 0) {
			choice = copper;
		} else {
			attempts = 0;
			while (attempts < 10) {
				choice = kingdomCards[(int) floor(Random() * SUPP)];
				if (getCost(choice) <= coins && state->supplyCount[choice]) {
					break;
				}
				attempts++;
			}
		}

		fprintf(f, "Buying card #%d\n", choice);
		buyCard(choice, state);
		buys = state->numBuys;
		willBuy = floor(Random() * 100);
	}
	return 0;
}

int main(int argc, char **argv)
{
	int players, c, stream = 1, seed = 1, r_len = 10, s_len = 10;
	int kingdomCards[SUPP];
	int verboseMode = 0;
	FILE *f;
	struct gameState state;
	int i, j;

	while ((c = getopt(argc, argv, "vr:s:")) != -1)
	{
		switch (c) {
		case 'v':
			f = stderr;
			verboseMode = 1;
			break;

		case 'r':
			stream = (int) optarg[0];
			r_len = 1;
			break;

		case 's':
			seed = (int) optarg[0];
			s_len = 1;
			break;

		default:
			printf("Unknown option: character code %o\n", c);
		}
	}

	if (verboseMode == 0) {
		char *fileName = "gameResults.out";
		f = fopen(fileName, "w+");
		if (f == NULL) {
			perror("File cannot be written to");
			exit(0);
		}
	}

	// Play games, incrementing the seed to 1000 first, then incrementing
	// stream and reseting seed by to 0 again

	for (i = stream; i < stream + r_len; i++) {
		for (j = seed; j < seed + s_len; j++) {
			SelectStream(i);
			PutSeed(j);
			Random(); //Burn through the first Random

			fprintf(f, "Testing Dominion: %d stream, seed %d\n", i, j);
		

			randomizeInitGame(&players, kingdomCards);
			initializeGame(players, kingdomCards, seed, &state);

			//Error with playedCount not being initialized to 0;
			state.playedCardCount = 0;

			while (isGameOver(&state) == 0) {
				fprintf(f, "---PLAYER %d ACTION TURN---------------\n", state.whoseTurn);
				outputState(&state, f);
				doAction(&state, kingdomCards, f);
				fprintf(f, "---PLAYER %d BUY TURN------------------\n", state.whoseTurn);
				doBuy(&state, kingdomCards, f);
				fprintf(f, "---PLAYER %d END TURN------------------\n", state.whoseTurn);
				outputState(&state, f);
				endTurn(&state);
			}
			outputState(&state, f);
		}
	}

	fprintf(f, "Test Complete\n");
	fclose(f);

	return 0;
}